
#include "linux_def.h"
#include "core.h"
#include "event.h"
#include "channel.h"

typedef struct{
	int	signo;
	char	*signame;
	void	(*handler)(int signo);
}signal_t;

static void signal_handler(int signo);
static void process_get_status();
int set_nonblocking(int fd);

int	process_slot;
int	g_channel;
int	process_last;
process_t	processes[MAX_PROCESSES];

signal_t signals[] = {
	{SIGHUP, "SIGHUP", signal_handler},	
	{SIGTERM, "SIGTERM", signal_handler},	
	{SIGQUIT, "SIGQUIT", signal_handler},	
	{SIGALRM, "SIGALRM", signal_handler},	
	{SIGINT, "SIGINT", signal_handler},	
	{SIGIO, "SIGIO", signal_handler},	
	{SIGCHLD, "SIGCHLD", signal_handler},	
	{SIGPIPE, "SIGPIPE", SIG_IGN},	
	{0, NULL, NULL}	
};

int  init_signals()
{
	signal_t	*sig;
	struct sigaction sa;

	for (sig=signals; sig->signo!=0; sig++){
		memset(&sa, 0, sizeof(struct sigaction));
		sa.sa_handler	= sig->handler;
		sigemptyset(&sa.sa_mask);
		if (sigaction(sig->signo, &sa, NULL) == -1){
			return -1;
		}
	}

	return 0;
}

void signal_handler(int signo)
{
	char	*action;
	int	ignore;
	signal_t	*sig;

	ignore = 0;

	for (sig=signals; sig->signo!=0; sig++){
		if (sig->signo == signo)
			break;
	}

	time_update(0, 0);

	switch(process){
		case PROCESS_MASTER:
		case PROCESS_SINGLE:
		switch(signo){
			case SIGQUIT:
				quit = 1;
				break;
			case SIGINT:
			case SIGTERM:
				terminate = 1;
				break;
			case SIGALRM:
				break;
			case SIGIO:
				sigio = 1;
				break;
			case SIGCHLD:
				reap = 1;
				break;
			default:
				break;
			}
			break;

		case PROCESS_WORKER:
		switch(signo){
			case SIGQUIT:
				quit = 1;
				break;
			case SIGINT:
			case SIGTERM:
				terminate = 1;
				break;
			default:
				break;
		}
		break;

		default:
			break;
	}

	if (signo == SIGCHLD)
		process_get_status();

	return;	
}

static void process_get_status()
{
	int	status;
	pid_t	pid;
	int	i, one=0;

	for (;;){
		//等待所有子进程结束
		pid = waitpid(-1, &status, WNOHANG);

		if (pid == 0)
			return;

		if (pid == -1){
			if (errno == EINTR)
				continue;

			//no child process
			if (errno == ECHILD && one)
				return;

			return;
		}

		one = 1;

		for (i=0; i<process_last; i++){
			if (processes[i].pid == pid){
				processes[i].status = status;
				processes[i].exited = 1;
				break;
			}
		}

		if (WTERMSIG(status) == 2 && processes[i].respawn){
			processes[i].respawn = 0;
		}
	}
}

int set_nonblocking(int fd)
{
	if (-1 == fcntl(fd, F_SETFL, fcntl(fd, F_GETFL) | O_NONBLOCK))
		return -1;
	return 0;
}


pid_t spawn_process(cycle_t *cycle, spawn_proc_pt proc, void *data, char *name, int respawn)
{
	pid_t	lpid;	
	int	s, on;

	if (respawn >= 0)
		s = respawn;
	else{
		for (s=0; s<process_last; s++){	//catch the free index
			if (processes[s].pid == -1)
				break;
		}
		if (s == MAX_PROCESSES){	//reached max processes
			fprintf(stderr, "no more than %d processes can be spawned.\n", MAX_PROCESSES);
			return -1;
		}
	}
	
	if (respawn != PROCESS_DETACHED){
		//create socketpair
		if (socketpair(AF_UNIX, SOCK_STREAM, 0, processes[s].channel) == -1){
			fprintf(stderr, "socketpair() failed.\n");
			return -1;
		}

	
		if (set_nonblocking(processes[s].channel[0]) == -1){
			fprintf(stderr, "set_nonblocking() failed.\n");
			close_channel(processes[s].channel);
			processes[s].channel[0] = -1;
			processes[s].channel[1] = -1;
			return -1;
		}
		if (set_nonblocking(processes[s].channel[1]) == -1){
			fprintf(stderr, "set_nonblocking() failed.\n");
			close_channel(processes[s].channel);
			processes[s].channel[0] = -1;
			processes[s].channel[1] = -1;
			return -1;
		}
	
		on = 1;
		if (ioctl(processes[s].channel[0], FIOASYNC, &on) == -1){
			fprintf(stderr, "ioctl() failed.\n");
			close_channel(processes[s].channel);
			processes[s].channel[0] = -1;
			processes[s].channel[1] = -1;
			return -1;
		}
	
		//set the channel[0] owner
		if (fcntl(processes[s].channel[0], F_SETOWN, g_pid) == -1){
			fprintf(stderr, "fcntl() failed.\n");
			close_channel(processes[s].channel);
			processes[s].channel[0] = -1;
			processes[s].channel[1] = -1;
			return -1;
		}
		
		//当用到exec启动的时候才会关闭
		if (fcntl(processes[s].channel[0], F_SETFD, FD_CLOEXEC) == -1){
			fprintf(stderr, "fcntl() failed.\n");
			close_channel(processes[s].channel);
			processes[s].channel[0] = -1;
			processes[s].channel[1] = -1;
			return -1;
		}
		if (fcntl(processes[s].channel[1], F_SETFD, FD_CLOEXEC) == -1){
			fprintf(stderr, "fcntl() failed.\n");
			close_channel(processes[s].channel);
			processes[s].channel[0] = -1;
			processes[s].channel[1] = -1;
			return -1;
		}

		g_channel = processes[s].channel[1];
	}else{
		processes[s].channel[0]	= -1;
		processes[s].channel[1] = -1;
	}

	process_slot = s;
	
	lpid = fork();
	switch(lpid){
		case -1:
			close_channel(processes[s].channel);
			processes[s].channel[0] = -1;
			processes[s].channel[1] = -1;
			return -1;
			break;
		case 0:
			g_pid = getpid();
			proc(cycle, data);
			break;
		default:
			break;

	}

	processes[s].pid	= lpid;
	processes[s].exited	= 0;

	printf("\n child %d  processes[%d].channels == [%d, %d]\n", lpid, s, processes[s].channel[0], processes[s].channel[1]);

	if (respawn >= 0)
		return lpid;

	processes[s].proc	= proc;
	processes[s].data	= data;
	processes[s].name	= name;
	processes[s].exiting	= 0;

	switch(respawn){
		case PROCESS_RESPAWN:
			processes[s].respawn	= 1;
			processes[s].just_spawn	= 0;
			processes[s].detached	= 0;
			break;
		case PROCESS_JUST_RESPAWN:
			processes[s].respawn	= 1;
			processes[s].just_spawn	= 1;
			processes[s].detached	= 0;
			break;
		case PROCESS_DETACHED:
			processes[s].respawn	= 0;
			processes[s].just_spawn	= 0;
			processes[s].detached	= 1;
			break;
		default:
			break;
	}

	if (s == process_last)
		process_last++;

	return lpid;
}


